///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once


#include "ExtrasPrerequisites.h "

#include "OgrePrerequisites.h"
#include "OgreString.h"
#include "OgreRenderable.h"
#include "OgreUTFString.h"
#include "OgreStringInterface.h"
#include "OverlayElementCommands.h"

#include "util/colourValue.h"
#include "math/rectangle.h"

namespace Extras {
	

#if OGRE_UNICODE_SUPPORT
	typedef UTFString DisplayString;
#	define OGRE_DEREF_DISPLAYSTRING_ITERATOR(it) it.getCharacter()
#else
	typedef Ogre::String DisplayString;
#	define OGRE_DEREF_DISPLAYSTRING_ITERATOR(it) *it
#endif
    
    enum GuiMetricsMode
    {
        /// 'left', 'top', 'height' and 'width' are parametrics from 0.0 to 1.0
        GMM_RELATIVE,
        /// Positions & sizes are in absolute pixels
        GMM_PIXELS,
        /// Positions & sizes are in virtual pixels
        GMM_RELATIVE_ASPECT_ADJUSTED
    };

    
    enum GuiHorizontalAlignment
    {
        GHA_LEFT,
        GHA_CENTER,
        GHA_RIGHT
    };
    
    enum GuiVerticalAlignment
    {
        GVA_TOP,
        GVA_CENTER,
        GVA_BOTTOM
    };

    
    class _PhiloExtrasExport OverlayElement : public StringInterface, public Renderable, public MemAlloc_SceneObj
    {
    public:

    protected:
        // Command object for setting / getting parameters
        static OverlayElementCommands::CmdLeft msLeftCmd;
        static OverlayElementCommands::CmdTop msTopCmd;
        static OverlayElementCommands::CmdWidth msWidthCmd;
        static OverlayElementCommands::CmdHeight msHeightCmd;
        static OverlayElementCommands::CmdMaterial msMaterialCmd;
        static OverlayElementCommands::CmdCaption msCaptionCmd;
        static OverlayElementCommands::CmdMetricsMode msMetricsModeCmd;
        static OverlayElementCommands::CmdHorizontalAlign msHorizontalAlignCmd;
        static OverlayElementCommands::CmdVerticalAlign msVerticalAlignCmd;
        static OverlayElementCommands::CmdVisible msVisibleCmd;


        Ogre::String mName;
        bool mVisible;
        bool mCloneable;
        Real mLeft;
        Real mTop;
        Real mWidth;
        Real mHeight;
        Ogre::String mMaterialName;
        MaterialPtr mMaterial;
        DisplayString mCaption;
        ColourValue mColour;
		Math::Rectangle mClippingRegion;

        GuiMetricsMode mMetricsMode;
        GuiHorizontalAlignment mHorzAlign;
        GuiVerticalAlignment mVertAlign;

        // metric-mode positions, used in GMM_PIXELS & GMM_RELATIVE_ASPECT_ADJUSTED mode.
        Real mPixelTop;
        Real mPixelLeft;
        Real mPixelWidth;
        Real mPixelHeight;
        Real mPixelScaleX;
        Real mPixelScaleY;

        // Parent pointer
        OverlayContainer* mParent;
        // Overlay attached to
        Overlay* mOverlay;

        // Derived positions from parent
        Real mDerivedLeft;
        Real mDerivedTop;
        bool mDerivedOutOfDate;

        /// Flag indicating if the vertex positions need recalculating
        bool mGeomPositionsOutOfDate;
		/// Flag indicating if the vertex uvs need recalculating
		bool mGeomUVsOutOfDate;

        // Zorder for when sending to render queue
        // Derived from parent
        ushort mZOrder;

        // world transforms
        Matrix4 mXForm;

        // is element enabled
        bool mEnabled;

		// is element initialised
		bool mInitialised;

        // Used to see if this element is created from a Template
        OverlayElement* mSourceTemplate ;

        
        virtual void updatePositionGeometry(void) = 0;
		
		virtual void updateTextureGeometry(void) = 0;

        
        virtual void addBaseParameters(void);

    public:
        /// Constructor: do not call direct, use OverlayManager::createElement
        OverlayElement(const Ogre::String& name);
        virtual ~OverlayElement();

        virtual void initialise(void) = 0;

        const Ogre::String& getName(void) const;

        virtual void show(void);

        virtual void hide(void);

        bool isVisible(void) const;

        bool isEnabled() const;
        virtual void setEnabled(bool b);

        void setDimensions(Real width, Real height);

        void setPosition(Real left, Real top);

        void setWidth(Real width);
        
        Real getWidth(void) const;

        void setHeight(Real height);
        
        Real getHeight(void) const;

        void setLeft(Real left);
        
        Real getLeft(void) const;

        void setTop(Real Top);
        
        Real getTop(void) const;

        Real _getLeft(void) const { return mLeft; }
        
        Real _getTop(void) const { return mTop; }
        
        Real _getWidth(void) const { return mWidth; }
        
        Real _getHeight(void) const { return mHeight; }
        
        void _setLeft(Real left);
        
        void _setTop(Real top);
        
        void _setWidth(Real width);
        
        void _setHeight(Real height);
        
        void _setPosition(Real left, Real top);
        
        void _setDimensions(Real width, Real height);

        
        virtual const Ogre::String& getMaterialName(void) const;

        
        virtual void setMaterialName(const Ogre::String& matName);


        // --- Renderable Overrides ---
        
        const MaterialPtr& getMaterial(void) const;

        // NB getRenderOperation not implemented, still abstract here

        
        void getWorldTransforms(Matrix4* xform) const;

        
        virtual void _positionsOutOfDate(void);

        
        virtual void _update(void);

        
        virtual void _updateFromParent(void);

        
        virtual void _notifyParent(OverlayContainer* parent, Overlay* overlay);

        
        virtual Real _getDerivedLeft(void);

        
        virtual Real _getDerivedTop(void);

		virtual Real _getRelativeWidth(void);
		
		virtual Real _getRelativeHeight(void);

		virtual void _getClippingRegion(Math::Rectangle &clippingRegion);

        virtual ushort _notifyZOrder(ushort newZOrder);

        virtual void _notifyWorldTransforms(const Matrix4& xform);

        virtual void _notifyViewport();

        virtual void _updateRenderQueue(RenderQueue* queue);

		/// @copydoc MovableObject::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, 
			bool debugRenderables = false);

        
        virtual const Ogre::String& getTypeName(void) const = 0;

        
		virtual void setCaption(const DisplayString& text);
        
        virtual const DisplayString& getCaption(void) const;
        
        virtual void setColour(const ColourValue& col);

        
        virtual const ColourValue& getColour(void) const;

        
        virtual void setMetricsMode(GuiMetricsMode gmm);
        
        virtual GuiMetricsMode getMetricsMode(void) const;
        
        virtual void setHorizontalAlignment(GuiHorizontalAlignment gha);
        
        virtual GuiHorizontalAlignment getHorizontalAlignment(void) const;
        
        virtual void setVerticalAlignment(GuiVerticalAlignment gva);
        
        virtual GuiVerticalAlignment getVerticalAlignment(void) const;

        virtual bool contains(Real x, Real y) const;

        
        virtual OverlayElement* findElementAt(Real x, Real y);		// relative to parent

        /**
        * returns false as this class is not a container type 
        */
        inline virtual bool isContainer() const
        { return false; }

        inline virtual bool isKeyEnabled() const
        { return false; }

        inline virtual bool isCloneable() const
        { return mCloneable; }

        inline virtual void setCloneable(bool c)
        { mCloneable = c; }

        /**
        * Returns the parent container.
        */
        OverlayContainer* getParent() ;
        void _setParent(OverlayContainer* parent) { mParent = parent; }

        /**
        * Returns the zOrder of the element
        */
        inline ushort getZOrder() const
        { return mZOrder; }

        
        Real getSquaredViewDepth(const Camera* cam) const 
        { 
            (void)cam;
            return 10000.0f - (Real)getZOrder(); 
        }

        
        const LightList& getLights(void) const
        {
            // Overlayelements should not be lit by the scene, this will not get called
            static LightList ll;
            return ll;
        }

        virtual void copyFromTemplate(OverlayElement* templateOverlay);
        virtual OverlayElement* clone(const Ogre::String& instanceName);

        // Returns the SourceTemplate for this element
        const OverlayElement* getSourceTemplate () const {
          return mSourceTemplate ;
        }
    };


}

